# Copyright (C) 2007 Insecure.Com LLC.
#
# Authors: Guilherme Polo <ggpolo@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 
# USA

import os
import gtk
import gobject
import ConfigParser
from gettext import gettext as _

import tl
from Calendar import weekdays
from Calendar import months
from Calendar import monthname
from Calendar import TL_Calendar


class Error(Exception):
    pass

class BadConfFile(Error):
    pass


# WARNING: these paths may work just for the demo!
conf_file = os.path.abspath(os.sys.path[0])+"/tl/tl.conf"
color_file_std = os.path.abspath(os.sys.path[0])+"/tl/colors_evt_std.conf"


def colors_from_file(color_file=conf_file):
    """
    Read colors from file and return a dict with colors.
    """
    cfg = ConfigParser.ConfigParser()
    cfg.read(color_file)

    colors = { }
    if cfg.has_section("Colors"):
        for t in cfg.items("Colors"):
            c = t[1].split(',')
            c = tuple([float(c) for c in c])
            colors[t[0]] = c
    else:
        raise BadConfFile(color_file)
    
    return colors


## Some colors that aren't supposed to be changed.
tl_colors = { "cell_bg_hover": (0, 0, 0, 1),
              "cell_fg_hover": (0.918, 0.91, 0.89, 1),
              "arrow_bg": (0, 0, 0, 1),
              "arrow_bg_hover": (0.918, 0.91, 0.89, 1),
              "shadow_from": (0.502, 0.49, 0.455, 0.05),
              "shadow_to": (0.337, 0.322, 0.282, 0.7)
}


def too_dark(rgb_color, level=320):
    """
    Returns True if an r, g, b color is too dark.
    This is bogus right now.
    """
    return sum((c * 255) for c in rgb_color) < level and True or False


def cairoc_to_gdkc(*colors):
    """
    Convers a "cairo color" in range 0..1 to "gdk color" in range 0..65535
    """
    return [int(c*65535) for c in colors]


def gdkc_to_cairoc(*colors):
    """
    Converts a "gdk color" in range 0..65535 to "cairo color" in range
    0..1
    """
    return [c/65535.0 for c in colors]
    

####
## TimeLine Base

class BadTimeLineInstance(Error):
    pass

def check_instance(instance):
    """
    Check for TimeLine instance
    """
    if type(instance) != tl.TimeLine.TL:
        raise BadTimeLineInstance(instance)
    else:
        return instance

class TL_Base(TL_Calendar):
    """
    Base class for TimeLine
    """
    
    def __init__(self, zoom=0, start_year=2007, start_month=3,
                 start_day=15, start_hour=23, fwday=0, 
                 year_range=(2006,2008), border_on_focus=True):
        
        
        TL_Calendar.__init__(self, start_year, start_month, start_day,
                             start_hour, year_range, fwday)
       
        self.set_colors()
        self.zoom = zoom
        self.draw_border = border_on_focus


    # TimeLine instance handler    
    def get_tl(self):
        """
        Return current TimeLine instance.
        """
        return self.__tl
    
    
    def set_tl(self, instance):
        """
        Set TimeLine instance.
        """
        self.__tl = check_instance(instance)
    

    # Title management    
    def title_by_zoom(self, hover_combine=False):
        """
        Returns a formatted event date based on current zoom level.
        """
        
        def adjust_date(date, multiply=False):
            if multiply:
                date *= 2
            
            if date < 10:
                date = "0%s" % date
            
            return date
        
        
        if hover_combine and self.hovering != - 1:
            fmtddate = ["%s's %s" % (monthname((self.hovering+1) % 12),
                                     self.year), # % 12 is there to not break when
                                                      # zoom != 0
                                "%s, %s's %s - %s" % (self.get_weekday(self.year,
                                                                       self.month, self.hovering+1)[1],
                                                    monthname(self.month), self.hovering+1, 
                                                    self.year),
                                "(%s:00) %s, %s's %s - %s" % (adjust_date(self.hovering),
                                                  self.get_current_weekday_name(),
                                                  monthname(self.month),
                                                  self.day, self.year),
                                "(%s:%s) %s, %s's %s - %s" % (self.hour, 
                                                              adjust_date(self.hovering, True),
                                                              self.get_current_weekday_name(),
                                                              monthname(self.month),
                                                              self.day, self.year)
                                ]
        
        else:
            fmtddate = [_("Year %s") % self.year, 
                    "%s's %s" % (monthname(self.month), self.year), 
                    "%s, %s's %s - %s" % (self.get_current_weekday_name(),
                                          monthname(self.month), self.day,
                                          self.year),
                    "(%s:00) %s, %s's %s - %s" % (self.hour,
                                                  self.get_current_weekday_name(),
                                                  monthname(self.month),
                                                  self.day, self.year) ]
        
        return fmtddate[self.zoom]
    
    
    # Color management
    def get_colors_evt(self):
        """
        Get current color set for events
        """
        return self.__colors_evt
    
    
    def get_colors_evt_descr(self):
        """
        Get events color key.
        """
        return self.__colors_evt_descr
    
    
    def set_colors(self):
        """
        Set colors for events.
        """
        self.__colors_evt = colors_from_file()
        self.__colors_evt_descr = self.__colors_evt.keys()
        self.emit('color-change-notify')
    
    
    # Date advance and retrocede
    def go_prev_date(self):
        """
        Decrement date based on zoom.
        """
        self.dec_date(self.zoom)
        self.emit('date-change-notify')
    
    
    def go_next_date(self):
        """
        Increment date based on zoom.
        """
        self.inc_date(self.zoom)
        self.emit('date-change-notify')

    
    # Border methods
    def get_border_draw(self):
        """
        Gets Draw border on focus.
        """
        return self.__draw_border
    
    
    def set_border_draw(self, draw):
        """
        Sets border to be draw or not when widget if focused.
        """
        self.__draw_border = draw
    

    # Highlight management methods
    def get_highlight(self, month_firstlast=0):
        """
        Returns dates to be highlighted based on current zoom level.
        
        0 = january, june, december
        1 = first weekdays plus first and last day of month
        2 = 0, 6, 12, 18, 23 hour
        3 = 0, 10, 20, 30, 40, 50, 58 minute
        """
        
        if month_firstlast:
            return self.get_current_firstlast_firstwday()[0]
        
        
        highlights = { 0: [1, 6, 12],
                       1: self.get_current_firstlast_firstwday()[2],
                       2: [0, 6, 12, 18, 23], 
                       3: [0, 10, 20, 30, 40, 50, 58]
                    }
        
        return highlights[self.zoom]
    
    
    def get_highlight_for(self, no):
        """
        Return highlight text.
        """
        highlights = { 0: { 1: months[0].upper(),  6: months[5].upper(),
                           12: months[11].upper() },
                       1: { -1: weekdays[self.get_first_weekday()].upper() },
                       2: { -1: "hour" },
                       3: { -1: "minute" },
                    }
        
        return highlights[self.zoom][(self.zoom == 0) and no or -1] \
                   [:(self.zoom >= 2) and 1 or 3]
    
    
    # Zoom management methods
    def adjust_number_display(self, no):
        """
        "Converts" number based on scale. Example:
        for scale = 0 (view by Year), we receive numbers in range 0..11,
        but months are in range 1..12, so we increment no by 1.
        """
        adjust = { 0: no + 1, 1: no + 1, 2: no, 3: no * 2 }
        return adjust[self.zoom] 


    def get_scale(self):
        """
        Returns scale based on zoom.
        """
        scale = { 0: 12, 1: self.get_current_monthrange()[1], 2: 24, 3: 30 }
        return scale[self.zoom]
    
    
    def can_zoom_out(self):
        """
        Returns True if zoom level isn't at minimum.
        """
        return self.zoom != 0 and True or False
    
    
    def zoom_out(self):
        """
        Zoom Out.
        """
        self.set_zoom(self.zoom - 1)
    
    
    def can_zoom_in(self):
        """
        Returns True if zoom level isn't at max.
        """
        return self.zoom != 3 and True or False
    
    
    def zoom_in(self):
        """
        Zoom In.
        """
        self.set_zoom(self.zoom + 1)
    
    
    def get_zoom(self):
        """
        Get Zoom level.
        """
        return self.__zoom
    
    
    def set_zoom(self, zoom_lvl):
        """
        Set Zoom level.
        """
        if not (0 <= zoom_lvl <= 3):
            old = zoom_lvl
            zoom_lvl %= 4
            # zoom level adjusted silently
                
        self.__zoom = zoom_lvl
        self.emit('date-change-notify')
    
    
    # Properties
    colors_evt = property(get_colors_evt)
    colors_evt_descr = property(get_colors_evt_descr)
    zoom = property(get_zoom, set_zoom)
    scale = property(get_scale)
    draw_border = property(get_border_draw, set_border_draw)
    